package com.lanchetech.admin.service.impl;


import com.alibaba.excel.util.CollectionUtils;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.lanchetech.admin.service.InvoiceService;
import com.lanchetech.admin.service.OrderService;
import com.lanchetech.bean.request.PageReq;
import com.lanchetech.bean.response.*;
import com.lanchetech.bean.util.PageHelpUtil;
import com.lanchetech.bean.vo.InvoiceApplyDetailVO;
import com.lanchetech.bean.vo.InvoiceApplyNuonuoDetailVO;
import com.lanchetech.bean.vo.InvoiceApplyNuonuoVO;
import com.lanchetech.bean.vo.InvoiceApplyVO;
import com.lanchetech.common.enums.*;
import com.lanchetech.common.util.StringHelper;
import com.lanchetech.dao.InvoiceAccountMapper;
import com.lanchetech.dao.InvoiceApplyMapper;
import com.lanchetech.dao.UserMapper;
import com.lanchetech.dao.WithdrawMapper;
import com.lanchetech.entity.InvoiceAccount;
import com.lanchetech.entity.InvoiceApply;
import com.lanchetech.entity.User;
import com.lanchetech.entity.Withdraw;
import nuonuo.open.sdk.NNOpenSDK;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service

public class InvoiceServiceImpl implements InvoiceService {

    @Autowired
    InvoiceApplyMapper invoiceApplyMapper;

    @Autowired
    InvoiceAccountMapper invoiceAccountMapper;

    @Autowired
    OrderService orderService;

    @Autowired
    UserMapper userMapper;

    @Value("${nuonuo.taxnum}")
    String taxnum;
    @Value("${nuonuo.appKey}")
    String appKey;
    @Value("${nuonuo.appSecret}")
    String appSecret;
    @Value("${nuonuo.url}")
    String url;
    @Value("${nuonuo.salerTel}")
    String salerTel;

    @Autowired
    WithdrawMapper withdrawMapper;

    @Autowired
    RedisTemplate redisTemplate;

    @Override
    public ResultData<BasePageResp<InvoiceApplyVO>> getInvoiceApplyPage(PageReq req) {
        Page page = PageHelper.startPage(req.getPageNo(), req.getPageSize());
        List<InvoiceApplyVO> list = invoiceApplyMapper.findAllPage(req);
        if (!CollectionUtils.isEmpty(list)) {
            List<InvoiceAccount> accountList = invoiceAccountMapper.findAllByIds(list.stream().map(InvoiceApply::getInvoiceAccountId).collect(Collectors.toList()));
            Map<Long, InvoiceAccount> map = new HashMap<>();
            accountList.stream().forEach(item -> map.put(item.getId(), item));
            list.stream().forEach(item -> {
                item.setInvoiceType(map.get(item.getInvoiceAccountId()).getInvoiceType());
                item.setTitleType(map.get(item.getInvoiceAccountId()).getTitleType());
                item.setTitle(map.get(item.getInvoiceAccountId()).getTitle());
            });
        }
        return PageHelpUtil.buildPage(list, page);
    }

    @Override
    public ResultData<InvoiceApplyDetailVO> getInvoiceApplyDetail(Long id) {
        InvoiceApplyDetailVO vo = new InvoiceApplyDetailVO();

        InvoiceApply invoiceApply = invoiceApplyMapper.selectByPrimaryKey(id);
        if (InvoiceApplyTypeEnum.ORDER.getType().equals(invoiceApply.getType())) {
            vo.setOrderResp(orderService.getOrderDetail(invoiceApply.getOrderId()).getValue());
        } else {
            vo.setWithdraw(withdrawMapper.selectByPrimaryKey(invoiceApply.getOrderId()));
        }
        vo.setInvoiceApply(invoiceApply);
        vo.setInvoiceAccount(invoiceAccountMapper.selectByPrimaryKey(invoiceApply.getInvoiceAccountId()));
        return new ResultData<>(vo);
    }

    @Override
    public BaseResp auditNuonuoInvoiceApply(InvoiceApply invoiceApply) {
        InvoiceApply resultApply = invoiceApplyMapper.selectByPrimaryKey(invoiceApply.getId());
        if (resultApply.getStatus().equals((byte) 1)) {
            return new BaseResp(ResultCode.FAIL);
        }
        BaseResp baseResp = new BaseResp();
        if(resultApply.getTenantId().equals(1L) || resultApply.getTenantId().equals(2L)){
            //
//        JSONObject jsonObject = getInvoice("21081514034801246091");
            JSONObject result = applyInvoice(resultApply);
            //  开具发票之后，不能立马得到结果，所有需要过一段时间再获取
            if (result.getString("code").equals("E0000")) {
                invoiceApply.setSerialNo(JSONObject.parseObject(result.getString("result")).getString("invoiceSerialNum"));
                invoiceApplyMapper.updateByPrimaryKeySelective(invoiceApply);
                getInvoice(invoiceApply.getSerialNo());
            } else {
                baseResp = new BaseResp(ResultCode.FAIL);
                baseResp.setMessage(result.getString("describe"));
            }
        }else {
            if(invoiceApply.getFileUrl() == null){
                baseResp.setResultCode(ResultCode.UPLOAD_FILE_URL);
            }
            invoiceApply.setFileUrl(invoiceApply.getFileUrl());
            invoiceApply.setDealTime(new Date());
            invoiceApply.setResultStatus(InvoiceResultStatusEnum.SUCCESS.getStatus());
            invoiceApply.setStatus(InvoiceStatusEnum.DONE.getStatus());
            invoiceApplyMapper.updateByPrimaryKeySelective(invoiceApply);
        }

        return baseResp;
    }

    @Override
    public BaseResp refreshInvoiceResultStatus(String serialNo) {
        NNOpenSDK nnsdk = NNOpenSDK.getIntance();
        String method = "nuonuo.ElectronInvoice.queryInvoiceResult";

        String content = "{\"orderNos\": [],\"serialNos\": [\"" + serialNo + "\"] }";
        String senid = UUID.randomUUID().toString().replace("-", "");
        String result = nnsdk.sendPostSyncRequest(url, senid, appKey, appSecret, getToken(), taxnum, method, content);
        BaseResp baseResp = new BaseResp();

        JSONObject newResult = JSONObject.parseObject(result);
        if (newResult.getString("code").equals("E0000")) {
            JSONArray eventInfoData = (JSONArray) newResult.get("result");
            JSONObject jsonArray = eventInfoData.getJSONObject(0);
            InvoiceApply invoiceApply = new InvoiceApply();
            if (jsonArray.getString("status").equals("2")) {
                invoiceApply.setSerialNo(jsonArray.getString("serialNo"));
                invoiceApply.setFileUrl(jsonArray.getString("pdfUrl"));
                invoiceApply.setDealTime(new Date());
                invoiceApply.setRemark(jsonArray.getString("statusMsg") + " " + jsonArray.getString("failCause"));
                invoiceApply.setResultStatus(InvoiceResultStatusEnum.SUCCESS.getStatus());
                invoiceApply.setStatus(InvoiceStatusEnum.DONE.getStatus());
                invoiceApplyMapper.updateBySerialNo(invoiceApply, serialNo);
            } else if (jsonArray.getString("status").equals("20") || jsonArray.getString("status").equals("21")) {
                invoiceApply.setRemark(jsonArray.getString("statusMsg") + " " + jsonArray.getString("failCause"));
                invoiceApply.setResultStatus(InvoiceResultStatusEnum.ONGOING.getStatus());
                invoiceApplyMapper.updateBySerialNo(invoiceApply, serialNo);
                baseResp = new BaseResp(ResultCode.FAIL);
                baseResp.setMessage(invoiceApply.getRemark());
            } else {
                invoiceApply.setRemark(jsonArray.getString("statusMsg") + " " + jsonArray.getString("failCause"));
                invoiceApply.setResultStatus(InvoiceResultStatusEnum.FAIL.getStatus());
                invoiceApplyMapper.updateBySerialNo(invoiceApply, serialNo);
                baseResp = new BaseResp(ResultCode.FAIL);
                baseResp.setMessage(invoiceApply.getRemark());
            }
        } else {
            baseResp.setCode(ResultCode.FAIL.getCode());
            baseResp.setMessage(newResult.getString("describe"));
        }
        return baseResp;
    }

    @Override
    public BaseResp againNuonuoInvoiceApply(String serialNo) {

        NNOpenSDK nnsdk = NNOpenSDK.getIntance();
        String method = "nuonuo.ElectronInvoice.reInvoice";

        String content = "{\"fpqqlsh\": \"" + serialNo + "\"}";
        String senid = UUID.randomUUID().toString().replace("-", "");
        String result = nnsdk.sendPostSyncRequest(url, senid, appKey, appSecret, getToken(), taxnum, method, content);
        JSONObject newResult = JSONObject.parseObject(result);

        BaseResp baseResp = new BaseResp();
        if (newResult.getString("code").equals("E0000")) {
            getInvoice(serialNo);
        } else {
            baseResp = new BaseResp(ResultCode.FAIL);
            baseResp.setMessage(newResult.getString("describe"));
        }
        return baseResp;
    }

    public String getToken() {
        ValueOperations<String, String> operations = redisTemplate.opsForValue();

        String token = operations.get("NuonuoToken");
        if (token != null) {
            return token;
        } else {
            String result = NNOpenSDK.getIntance().getMerchantToken(appKey, appSecret);
            JSONObject newResult = JSONObject.parseObject(result);

            String tokenResult = newResult.getString("access_token");
            operations.set("NuonuoToken", tokenResult, 1, TimeUnit.DAYS);

            return tokenResult;
        }
    }

    public JSONObject applyInvoice(InvoiceApply invoiceApply) {
        String salerAddress="浙江省杭州市滨江区长河街道滨兴路1866号2号楼403室";
        String salerAccount="中国建设银行股份有限公司杭州萍水路支行33050161618800000127";
        String clerk="陈苏青";

        NNOpenSDK nnsdk = NNOpenSDK.getIntance();
        String method = "nuonuo.ElectronInvoice.requestBillingNew";

        List<InvoiceApplyNuonuoDetailVO> invoiceDetailList = new ArrayList<>();

        String tradeNo = "";
        if (InvoiceApplyTypeEnum.ORDER.getType().equals(invoiceApply.getType())) {
            // 获取商品信息
            OrderResp orderDetail = orderService.getOrderDetail(invoiceApply.getOrderId()).getValue();


            if (invoiceApply.getContentType() == 0) {
                orderDetail.getOrderSkuList().stream().forEach(item -> {
                    invoiceDetailList.add(InvoiceApplyNuonuoDetailVO.builder()
                            .goodsName(StringHelper.subPayBodyString(item.getProductName()))
                            .price(String.valueOf(item.getPrice()))
                            .num(String.valueOf(item.getCount()))
                            .taxRate(invoiceApply.getTaxRate().toString())
                            .withTaxFlag("1")
                            .build());
                });
            } else {
                invoiceDetailList.add(InvoiceApplyNuonuoDetailVO.builder()
                        .goodsCode("106040604")
                        .goodsName("智能健身商品")
                        .taxRate(invoiceApply.getTaxRate().toString())
                        .tax(String.valueOf(invoiceApply.getTax()))
                        .taxExcludedAmount(String.valueOf(orderDetail.getTotalPayAmount().divide(new BigDecimal("1.13"), 2, BigDecimal.ROUND_HALF_DOWN)))
                        .taxIncludedAmount(String.valueOf(orderDetail.getTotalPayAmount()))
                        .withTaxFlag("1")
                        .build());
            }
            tradeNo = orderDetail.getTradeNo();

        } else {
            InvoiceApply result = invoiceApplyMapper.selectByPrimaryKey(invoiceApply.getId());
            Withdraw withdraw = withdrawMapper.selectByPrimaryKey(result.getOrderId());
            invoiceDetailList.add(InvoiceApplyNuonuoDetailVO.builder()
                    .goodsCode("106040604")
                    .goodsName("消费")
                    .taxRate(invoiceApply.getTaxRate().toString())
                    .tax(String.valueOf(invoiceApply.getTax()))
                    .taxExcludedAmount(String.valueOf(withdraw.getAmount().divide(new BigDecimal("1.13"), 2, BigDecimal.ROUND_HALF_DOWN)))
                    .taxIncludedAmount(String.valueOf(withdraw.getAmount()))
                    .withTaxFlag("1")
                    .build());

            tradeNo = result.getTradeNo();
        }

        // 填写发票信息
        DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        InvoiceAccount invoiceAccount = invoiceAccountMapper.selectByPrimaryKey(invoiceApply.getInvoiceAccountId());
        User user = userMapper.selectByPrimaryKey(invoiceApply.getUserId());
        InvoiceApplyNuonuoVO order = InvoiceApplyNuonuoVO.builder()
                .buyerName(invoiceAccount.getTitle())
                .buyerTaxNum(invoiceAccount.getTaxNumber())
                .buyerAddress(invoiceAccount.getAddress())
                .buyerTel(invoiceAccount.getPhone())
                .buyerAccount((invoiceAccount.getBankAccount() != null ? invoiceAccount.getBankAccount() : " ") + " " + (invoiceAccount.getOpenBank() != null ? invoiceAccount.getOpenBank() : " "))
                .salerTaxNum(taxnum)
                .salerTel(salerTel)
                .salerAddress(salerAddress)
                .salerAccount(salerAccount)
                .orderNo(tradeNo)
                .invoiceDate(sdf.format(new Date()))
                .clerk(clerk)
                .pushMode("1") // 推送方式：-1,不推送;0,邮箱;1,手机（默认* ）;2,邮箱、手机
                .buyerPhone(user.getUsername())
                .email(invoiceAccount.getEmail())
                .invoiceType("1")
                .invoiceDetail(invoiceDetailList)
                .build();

        if (invoiceAccount.getInvoiceType().equals(InvoiceTypeEnum.S.getType())) {
            order.setInvoiceLine("s");
        }

        String content = nnsdk.convertToUtf8("{\"order\":" + JSONObject.toJSONString(order) + "}");
        String senid = UUID.randomUUID().toString().replace("-", "");
        String result = nnsdk.sendPostSyncRequest(url, senid, appKey, appSecret, getToken(), taxnum, method, content);
        JSONObject newResult = JSONObject.parseObject(result);
        return newResult;
    }

    @Async
    public void getInvoice(String serialNo) {
        final int[] count = {0};
        Timer timer = new Timer();
        TimerTask task = new TimerTask() {
            @Override
            public void run() {
                count[0] += 1;
                if (count[0] == 3) {
                    timer.cancel();
                }
                NNOpenSDK nnsdk = NNOpenSDK.getIntance();
                String method = "nuonuo.ElectronInvoice.queryInvoiceResult";

                String content = "{\"orderNos\": [],\"serialNos\": [\"" + serialNo + "\"] }";
                String senid = UUID.randomUUID().toString().replace("-", ""); // 唯一标识，32位随机码，无需修改，保持默认即可
                String result = nnsdk.sendPostSyncRequest(url, senid, appKey, appSecret, getToken(), taxnum, method, content);
                InvoiceApply invoiceApply = new InvoiceApply();
                JSONObject newResult = JSONObject.parseObject(result);
                if (newResult.getString("code").equals("E0000")) {
                    JSONArray eventInfoData = (JSONArray) newResult.get("result");
                    JSONObject jsonArray = eventInfoData.getJSONObject(0);
                    if (jsonArray.getString("status").equals("2")) {
                        invoiceApply.setSerialNo(jsonArray.getString("serialNo"));
                        invoiceApply.setFileUrl(jsonArray.getString("pdfUrl"));
                        invoiceApply.setDealTime(new Date());
                        invoiceApply.setRemark(jsonArray.getString("statusMsg") + " " + jsonArray.getString("failCause"));
                        invoiceApply.setResultStatus(InvoiceResultStatusEnum.SUCCESS.getStatus());
                        invoiceApply.setStatus(InvoiceStatusEnum.DONE.getStatus());
                        invoiceApplyMapper.updateBySerialNo(invoiceApply, serialNo);
                        timer.cancel();
                    } else if (jsonArray.getString("status").equals("20") || jsonArray.getString("status").equals("21")) {
                        invoiceApply.setRemark(jsonArray.getString("statusMsg") + " " + jsonArray.getString("failCause"));
                        invoiceApply.setResultStatus(InvoiceResultStatusEnum.ONGOING.getStatus());
                        invoiceApplyMapper.updateBySerialNo(invoiceApply, serialNo);
                    } else {
                        invoiceApply.setRemark(jsonArray.getString("statusMsg") + " " + jsonArray.getString("failCause"));
                        invoiceApply.setResultStatus(InvoiceResultStatusEnum.FAIL.getStatus());
                        invoiceApplyMapper.updateBySerialNo(invoiceApply, serialNo);
                    }
                }
            }
        };
        timer.schedule(task, 5000, 20000);
    }
}